using System;
using System.Text;
using System.Threading;

namespace org.jawk.ext
{
	/// <summary>
	/// Extensions which make developing in Jawk and
	/// interfacing other extensions with Jawk
	/// much easier.
	/// </summary>
	/// <remarks>
	/// Extensions which make developing in Jawk and
	/// interfacing other extensions with Jawk
	/// much easier.
	/// <p>
	/// The extension functions which are available are as follows:
	/// <ul>
	/// <li><strong>Array</strong> - <code><font size=+1>Array(array,1,3,5,7,9)</font></code><br />
	/// Inserts elements into an associative array whose keys
	/// are ordered non-negative integers, and the values
	/// are the arguments themselves.  The first argument is
	/// the assocative array itself.
	/// <li><strong>Map/HashMap/TreeMap/LinkedMap</strong> - <code><font size=+1>Map(map,k1,v1,k2,v2,...,kN,vN)</font></code>,
	/// or <code><font size=+1>Map(k1,v1,k2,v2,...,kN,vN)</font></code>.<br />
	/// Build an associative array with its keys/values as
	/// parameters.  The odd parameter count version takes
	/// the map name as the first parameter, while the even
	/// parameter count version returns an anonymous associative
	/// array for the purposes of providing a map by function
	/// call parameter.<br />
	/// Map/HashMap configures the assocative array as a
	/// hash map, TreeMap as an ordered map, and LinkedMap
	/// as a map which traverses the key set in order of
	/// insertion.
	/// <li><strong>MapUnion</strong> - <code><font size=+1>MapUnion(map,k1,v1,k2,v2,...,kN,vN)</font></code><br />
	/// Similar to Map, except that map is not cleared prior
	/// to populating it with key/value pairs from the
	/// parameter list.
	/// <li><strong>MapCopy</strong> - <code><font size=+1>cnt = MapCopy(aa_target, aa_source)</font></code><br />
	/// Clears the target associative array and copies the
	/// contents of the source associative array to the
	/// target associative array.
	/// <li><strong>TypeOf</strong> - <code><font size=+1>typestring = TypeOf(item)</font></code><br />
	/// Returns one of the following depending on the argument:
	/// <ul>
	/// <li>"String"
	/// <li>"Integer"
	/// <li>"AssocArray"
	/// <li>"Reference" (see below)
	/// </ul>
	/// <li><strong>String</strong> - <code><font size=+1>str = String(3)</font></code><br />
	/// Converts its argument to a String.
	/// Similar to the _STRING extension, but provided
	/// for completeness/normalization.
	/// <li><strong>Double</strong> - <code><font size=+1>dbl = Double(3)</font></code><br />
	/// Converts its argument ot a Double.
	/// Similar to the _DOUBLE extension, but provided
	/// for completeness/normalization.
	/// <li><strong>Halt</strong> - <code><font size=+1>Halt()</font></code><br />
	/// Similar to exit(), except that END blocks are
	/// not executed if Halt() called before END
	/// block processing.
	/// <li><strong>Timeout</strong> - <code><font size=+1>r = Timeout(300)</font></code><br />
	/// A blocking function which waits N milliseconds
	/// before unblocking (continuing).  This is useful in scripts which
	/// employ blocking, but occasionally needs to break out
	/// of the block to perform some calculation, polling, etc.
	/// <li><strong>Throw</strong> - <code><font size=+1>Throw("this is an awkruntimeexception")</font></code><br />
	/// Throws an AwkRuntimeException from within the script.
	/// <li><strong>Version</strong> - <code><font size=+1>print Version(aa)</font></code><br />
	/// Prints the version of the Java class which represents the parameter.
	/// <li><strong>Date</strong> - <code><font size=+1>str = Date()</font></code><br />
	/// Similar to the Java equivalent : str = new Date().toString();
	/// <li><strong>FileExists</strong> - <code><font size=+1>b = FileExists("/a/b/c")</font></code><br />
	/// Returns 0 if the file doesn't exist, 1 otherwise.
	/// <li><strong>NewRef[erence]/Dereference/DeRef/Unreference/UnRef/etc.</strong> -
	/// Reference Management Functions.</font></code><br />
	/// These are described in detail below.
	/// </ul>
	/// <p>
	/// <h1>Reference Management</h1>
	/// AWK's memory model provides only 4 types of variables
	/// for use within AWK scripts:
	/// <ul>
	/// <li>Integer
	/// <li>Double
	/// <li>String
	/// <li>Associative Array
	/// </ul>
	/// Variables can hold any of these types.  However, unlike
	/// for scalar types (integer/double/string), AWK applies
	/// the following restrictions with regard to associative
	/// arrays:
	/// <ul>
	/// <li>Associative array assignments (i.e., assocarray1 = associarray2)
	/// are prohibited.
	/// <li>Functions cannot return associative arrays.
	/// </ul>
	/// These restrictions, while sufficient for AWK, are detrimental
	/// to extensions because associative arrays are excellent vehicles
	/// for configuration and return values for user extensions.
	/// Plus, associative arrays can be subclassed, which can be used
	/// to enforce type safety within user extensions.  Unfortunately, the
	/// memory model restrictions make using associative arrays in this
	/// capacity very difficult.
	/// <p>
	/// We attempt to alleviate these difficulties by adding references
	/// to Jawk via the CoreExtension module.
	/// References convert associative arrays into
	/// unique strings called <strong>reference handles</strong>.
	/// Since reference handles are strings, they can be
	/// assigned and returned via AWK functions without restriction.
	/// And, reference handles are then used by other reference extension
	/// functions to perform common associative array operations, such as
	/// associative array cell lookup and assignment, key existence
	/// check, and key iteration.
	/// <p>
	/// The reference model functions are explained below:
	/// <ul>
	/// <li><strong>NewRef / NewReference</strong> - <code><font size=+1>handle = NewRef(assocarray)</font></code><br />
	/// Store map into reference cache.  Return the unique string handle
	/// for this associative array.
	/// <li><strong>DeRef / Dereference</strong> - <code><font size=+1>val = DeRef(handle, key)</font></code><br />
	/// Return the cell value of the associative array referenced by the key.
	/// In other words:
	/// <blockquote><pre>
	/// return assocarray[key]</pre></blockquote>
	/// <li><strong>UnRef / Unreference</strong> - <code><font size=+1>UnRef(handle)</font></code><br />
	/// Eliminate the reference occupied by the reference cache.
	/// <li><strong>InRef</strong> - <code><font size=+1>while(key = InRef(handle)) ...</font></code><br />
	/// Iterate through the keyset of the associative array
	/// referred to by handle in the reference cache.
	/// This is similar to:
	/// <blockquote><pre>
	/// for (key in assocarray)
	/// ...</pre></blockquote>
	/// where assocarray is the associative array referred to by
	/// handle in the reference cache.
	/// <br />
	/// <strong>Warning:</strong> unlike the IN keyword, InRef
	/// will maintain state regardless of scope.  That is,
	/// if one were to break; out of the while loop above,
	/// the next call to InRef() will be the next anticipated
	/// element of the assoc array.
	/// <li><strong>IsInRef</strong> - <code><font size=+1>b = IsInRef(handle, key)</font></code><br />
	/// Checks whether the associative array in the reference cache
	/// contains the key.  This is similar to:
	/// <blockquote><pre>
	/// if (key in assocarray)
	/// ...</pre></blockquote>
	/// where assocarray is the associative array referred to by
	/// handle in the reference cache.
	/// <li><strong>DumpRefs</strong> - <code><font size=+1>DumpRefs()</font></code><br />
	/// Dumps the reference cache to stdout.
	/// </ul>
	/// </remarks>
	public class CoreExtension : org.jawk.ext.AbstractExtension, org.jawk.ext.IJawkExtension
	{
		private static org.jawk.ext.CoreExtension instance = null;

		private static readonly object INSTANCE_LOCK = new object();

		public CoreExtension()
		{
			timeout_blocker = new _BlockObject_653(this);
			lock (INSTANCE_LOCK)
			{
				if (instance == null)
				{
					instance = this;
				}
				else
				{
					//System.Console.Error.WriteLine("Warning : Multiple CoreExtension instances in this VM.  Using original instance.");
				}
			}
		}

		// to satisfy the JawkExtension interface
		public override string GetExtensionName()
		{
			return "Core Extension";
		}

		public override string[] ExtensionKeywords()
		{
			return new string[] { "Array", "Map", "HashMap", "TreeMap", "LinkedMap", "MapUnion"
				, "MapCopy", "TypeOf", "String", "Double", "Halt", "Dereference", "DeRef", "NewReference"
				, "NewRef", "Unreference", "UnRef", "InRef", "IsInRef", "DumpRefs", "Timeout", "Throw"
				, "Version", "Date", "FileExists" };
		}

		// i.e.  Array(array,1,3,5,7,9,11)
		// i.e.  Map(assocarray, "hi", "there", "testing", 3, 5, Map("item1", "item2", "i3", 4))
		// i.e.  HashMap(assocarray, "hi", "there", "testing", 3, 5, Map("item1", "item2", "i3", 4))
		// i.e.  TreeMap(assocarray, "hi", "there", "testing", 3, 5, Map("item1", "item2", "i3", 4))
		// i.e.  LinkedMap(assocarray, "hi", "there", "testing", 3, 5, Map("item1", "item2", "i3", 4))
		// i.e.  MapUnion(assocarray, "hi", "there", "testing", 3, 5, Map("item1", "item2", "i3", 4))
		// i.e.  cnt = MapCopy(aa_target, aa_source)
		// i.e.  typestring = TypeOf(item)
		// i.e.  str = String(3)
		// i.e.  dbl = Double(3)
		// i.e.  Halt()
		// i.e.  f(Dereference(r1))
		// i.e.  	(see above, but replace Dereference with DeRef)
		// i.e.  ref = NewReference(Map("hi","there"))
		// i.e.  	(see above, but replace Reference with Ref)
		// i.e.  b = Unreference(ref)
		// i.e.  	(see above, but replace Unreference with UnRef)
		// i.e.  while(k = InRef(r2)) [ same as for(k in assocarr) ]
		// i.e.  if (IsInRef(r1, "key")) [ same as if("key" in assocarr) ]
		// i.e.  DumpRefs()
		// i.e.  r = Timeout(300)
		// i.e.  Throw("this is an awkruntimeexception")
		// i.e.  print Version(aa)
		// i.e.  str = Date()
		// i.e.  b = FileExists("/a/b/c")
		public override int[] GetAssocArrayParameterPositions(string extension_keyword, int
			 num_args)
		{
			if ((false || extension_keyword.Equals("Map") || extension_keyword.Equals("HashMap"
				) || extension_keyword.Equals("LinkedMap") || extension_keyword.Equals("TreeMap"
				)) && num_args % 2 == 1)
			{
				// first argument of a *Map() function
				// must be an associative array
				return new int[] { 0 };
			}
			else
			{
				if (false || extension_keyword.Equals("Array"))
				{
					// first argument of Array must be
					// an associative array
					return new int[] { 0 };
				}
				else
				{
					if (false || extension_keyword.Equals("NewReference") || extension_keyword.Equals
						("NewRef"))
					{
						if (num_args == 1)
						{
							return new int[] { 0 };
						}
						else
						{
							return base.GetAssocArrayParameterPositions(extension_keyword, num_args);
						}
					}
					else
					{
						return base.GetAssocArrayParameterPositions(extension_keyword, num_args);
					}
				}
			}
		}

		public override object Invoke(string keyword, object[] args)
		{
			
				if (keyword.Equals("Map") || keyword.Equals("HashMap"))
				{
					return Map(args, vm, org.jawk.jrt.AssocArray.MT_HASH);
				}
				else
				{
					if (keyword.Equals("LinkedMap"))
					{
						return Map(args, vm, org.jawk.jrt.AssocArray.MT_LINKED);
					}
					else
					{
						if (keyword.Equals("TreeMap"))
						{
							return Map(args, vm, org.jawk.jrt.AssocArray.MT_TREE);
						}
						else
						{
							if (keyword.Equals("MapUnion"))
							{
								return Mapunion(args, vm, org.jawk.jrt.AssocArray.MT_LINKED);
							}
							else
							{
								if (keyword.Equals("MapCopy"))
								{
									CheckNumArgs(args, 2);
									return Mapcopy(args);
								}
								else
								{
									if (keyword.Equals("Array"))
									{
										return Array(args, vm);
									}
									else
									{
										if (keyword.Equals("TypeOf"))
										{
											CheckNumArgs(args, 1);
											return Typeof(args[0], vm);
										}
										else
										{
											if (keyword.Equals("String"))
											{
												CheckNumArgs(args, 1);
												return Tostring(args[0], vm);
											}
											else
											{
												if (keyword.Equals("Double"))
												{
													CheckNumArgs(args, 1);
													return Todouble(args[0], vm);
												}
												else
												{
													if (keyword.Equals("Halt"))
													{
														if (args.Length == 0)
														{
                                                            Environment.Exit(0);
														}
														else
														{
															if (args.Length == 1)
															{
                                                                Environment.Exit((int)org.jawk.jrt.JRT.ToDouble(args[0]));
															}
															else
															{
																throw new org.jawk.jrt.IllegalAwkArgumentException(keyword + " requires 0 or 1 argument, not "
																	 + args.Length);
															}
														}
													}
													else
													{
														if (keyword.Equals("NewReference") || keyword.Equals("NewRef"))
														{
															if (args.Length == 1)
															{
																return Newreference(args[0]);
															}
															else
															{
																if (args.Length == 3)
																{
																	return Newreference(ToAwkString(args[0]), args[1], args[2]);
																}
																else
																{
																	throw new org.jawk.jrt.IllegalAwkArgumentException(keyword + " requires 1 or 3 arguments, not "
																		 + args.Length);
																}
															}
														}
														else
														{
															if (keyword.Equals("Dereference") || keyword.Equals("DeRef"))
															{
																if (args.Length == 1)
																{
																	return Resolve(Dereference(args[0], vm), vm);
																}
																else
																{
																	if (args.Length == 2)
																	{
																		return Resolve(Dereference(ToAwkString(args[0]), args[1], vm), vm);
																	}
																	else
																	{
																		throw new org.jawk.jrt.IllegalAwkArgumentException(keyword + " requires 1 or 2 arguments, not "
																			 + args.Length);
																	}
																}
															}
															else
															{
																if (keyword.Equals("Unreference") || keyword.Equals("UnRef"))
																{
																	CheckNumArgs(args, 1);
																	return Unreference(args[0], vm);
																}
																else
																{
																	if (keyword.Equals("InRef"))
																	{
																		CheckNumArgs(args, 1);
																		return Inref(args[0], vm);
																	}
																	else
																	{
																		if (keyword.Equals("IsInRef"))
																		{
																			CheckNumArgs(args, 2);
																			return Isinref(args[0], args[1], vm);
																		}
																		else
																		{
																			if (keyword.Equals("DumpRefs"))
																			{
																				CheckNumArgs(args, 0);
																				Dumprefs();
																			}
																			else
																			{
																				if (keyword.Equals("Timeout"))
																				{
																					CheckNumArgs(args, 1);
																					return Timeout((int)org.jawk.jrt.JRT.ToDouble(args[0]));
																				}
																				else
																				{
																					if (keyword.Equals("Throw"))
																					{
                                                                                        StringBuilder sb = new StringBuilder();
                                                                                        foreach(object o in args)
                                                                                        {
                                                                                            if (sb.Length > 0) sb.Append(Environment.NewLine);
                                                                                            sb.Append(o.ToString());
                                                                                        }
																						throw new org.jawk.jrt.AwkRuntimeException(sb.ToString());
																					}
																					else
																					{
																						if (keyword.Equals("Version"))
																						{
																							CheckNumArgs(args, 1);
																							return Version(args[0]);
																						}
																						else
																						{
																							if (keyword.Equals("Date"))
																							{
																								if (args.Length == 0)
																								{
																									return Date();
																								}
																								else
																								{
																									if (args.Length == 1)
																									{
																										return Date(ToAwkString(args[0]));
																									}
																									else
																									{
																										throw new org.jawk.jrt.IllegalAwkArgumentException(keyword + " expects 0 or 1 argument, not "
																											 + args.Length);
																									}
																								}
																							}
																							else
																							{
																								if (keyword.Equals("FileExists"))
																								{
																									CheckNumArgs(args, 1);
																									return Fileexists(ToAwkString(args[0]));
																								}
																								else
																								{
																									throw new org.jawk.NotImplementedError(keyword);
																								}
																							}
																						}
																					}
																				}
																			}
																		}
																	}
																}
															}
														}
													}
												}
											}
										}
									}
								}
							}
						}
					}
				}
			
			// never reached
			return null;
		}

		private object Resolve(object arg, org.jawk.jrt.IVariableManager vm)
		{
			while (true)
			{
				if (arg is org.jawk.jrt.AssocArray)
				{
					return arg;
				}
				string arg_check = ToAwkString(arg);
				if (reference_map.ContainsKey(arg_check))
				{
					arg = reference_map[arg_check];
				}
				else
				{
					return arg;
				}
			}
		}

		private int refmap_idx = 0;

		private System.Collections.Generic.IDictionary<string, object> reference_map = new 
			System.Collections.Generic.Dictionary<string, object>();

		internal static string Newreference(object arg)
		{
			if (!(arg is org.jawk.jrt.AssocArray))
			{
				throw new org.jawk.jrt.IllegalAwkArgumentException("NewRef[erence] requires an assoc array, not "
					 + arg.GetType().FullName);
			}
			// otherwise, set the reference and return the new key
			// get next refmap_idx
			int r_idx = instance.refmap_idx++;
			// inspect the argument
			string arg_string;
			if (arg is org.jawk.jrt.AssocArray)
			{
				arg_string = arg.GetType().FullName;
			}
			else
			{
				arg_string = arg.ToString();
			}
			if (arg_string.Length > 63)
			{
                arg_string = arg_string.Substring(0, 60) + "...";
			}
			// build Reference (scalar) string to this argument
			string retval = "@REFERENCE@ " + r_idx + " <" + arg_string + ">";
			instance.reference_map[retval] = arg;
			return retval;
		}

		// this version assigns an assoc array a key/value pair
		internal static object Newreference(string refstring, object key, object value)
		{
			org.jawk.jrt.AssocArray aa = null;
            if (instance.reference_map.ContainsKey(refstring))
            {
                aa = (org.jawk.jrt.AssocArray)instance.reference_map[refstring];
            }
		    if (aa == null)
			{
				throw new org.jawk.jrt.IllegalAwkArgumentException("AssocArray reference doesn't exist.");
			}
			return aa.Put(key, value);
		}

		// this version assigns an object to a reference
		private object Dereference(object arg, org.jawk.jrt.IVariableManager vm)
		{
			// return the reference if the arg is a reference key
			if (arg is org.jawk.jrt.AssocArray)
			{
				throw new org.jawk.jrt.IllegalAwkArgumentException("an assoc array cannot be a reference handle"
					);
			}
			else
			{
				string arg_check = ToAwkString(arg);
				return Dereference(arg_check);
			}
		}

		// split this out for static access by other extensions
		internal static object Dereference(string arg_check)
		{
			if (instance.reference_map.ContainsKey(arg_check))
			{
				return instance.reference_map[arg_check];
			}
			else
			{
				throw new org.jawk.jrt.IllegalAwkArgumentException(arg_check + " not a valid reference"
					);
			}
		}

		// this version assumes an assoc array is stored as a reference,
		// and to retrieve the stored value
		internal static object Dereference(string refstring, object key, org.jawk.jrt.IVariableManager
			 vm)
		{
			org.jawk.jrt.AssocArray aa = null;
            if (instance.reference_map.ContainsKey(refstring))
            {
                aa = (org.jawk.jrt.AssocArray)instance.reference_map[refstring];
            }
		    if (aa == null)
			{
				throw new org.jawk.jrt.IllegalAwkArgumentException("AssocArray reference doesn't exist."
					);
			}
			if (!(key is org.jawk.jrt.AssocArray))
			{
				// check if key is a reference string!
				string key_check = instance.ToAwkString(key);
				if (instance.reference_map.ContainsKey(key_check))
				{
					// assume it is a reference rather than an assoc array key itself
					key = instance.reference_map[key_check];
				}
			}
			return aa.Get(key);
		}

		internal static int Unreference(object arg, org.jawk.jrt.IVariableManager vm)
		{
			string arg_check = instance.ToAwkString(arg);
			if (!instance.reference_map.ContainsKey(arg_check))
			{
				throw new org.jawk.jrt.IllegalAwkArgumentException("Not a reference : " + arg_check);
			}
            instance.reference_map.Remove(arg_check);
			//assert instance.reference_map.get(arg_check) == null;
			return 1;
		}

		private System.Collections.Generic.IDictionary<org.jawk.jrt.AssocArray, System.Collections.IEnumerator
			> iterators = new System.Collections.Generic.Dictionary<org.jawk.jrt.AssocArray, 
			System.Collections.IEnumerator>();

		private object Inref(object arg, org.jawk.jrt.IVariableManager vm)
		{
			if (arg is org.jawk.jrt.AssocArray)
			{
				throw new org.jawk.jrt.IllegalAwkArgumentException("InRef requires a Reference (string) argument, not an assoc array"
					);
			}
			string arg_check = ToAwkString(arg);
			if (!reference_map.ContainsKey(arg_check))
			{
				throw new org.jawk.jrt.IllegalAwkArgumentException("Not a reference : " + arg_check);
			}
			object o = reference_map[arg_check];
			if (!(o is org.jawk.jrt.AssocArray))
			{
				throw new org.jawk.jrt.IllegalAwkArgumentException("Reference not an assoc array.  ref.class = "
					 + o.GetType().FullName);
			}
			org.jawk.jrt.AssocArray aa = (org.jawk.jrt.AssocArray)o;
			// use an in_map to keep track of existing iterators
			//Iterator<Object> iter = iterators.get(aa);
			System.Collections.IEnumerator iter = null;
            if (iterators.ContainsKey(aa))
            {
                iter = iterators[aa];
            }

		    if (iter == null)
			{
				//iterators.put(aa, iter = aa.keySet().iterator());
				// without a new Collection, modification to the
				// assoc array during iteration causes a ConcurrentModificationException
				iterators[aa] = iter = new System.Collections.Generic.List<object>(aa.KeySet()).GetEnumerator
					();
			}
			object retval = null;
			while (iter.MoveNext())
			{
				retval = iter.Current;
				if (retval is string && retval.ToString().Equals(string.Empty))
				{
					throw new org.jawk.jrt.AwkRuntimeException("Assoc array key contains a blank string ?!"
						);
				}
				break;
			}
			if (retval == null)
			{
                iterators.Remove(aa);
				retval = string.Empty;
			}
			if (retval is org.jawk.jrt.AssocArray)
			{
				// search if item is referred to already
				foreach (string @ref in reference_map.Keys)
				{
					if (reference_map[@ref] == retval)
					{
						return @ref;
					}
				}
				// otherwise, return new reference to this item
				//return newreference(arg_check, retval);
				return Newreference(retval);
			}
			else
			{
				return retval;
			}
		}

		private static readonly int ZERO = 0;

		private static readonly int ONE = 1;

		private int Isinref(object @ref, object key, org.jawk.jrt.IVariableManager vm)
		{
			if (@ref is org.jawk.jrt.AssocArray)
			{
				throw new org.jawk.jrt.IllegalAwkArgumentException("Expecting a reference string for the 1st argument, not an assoc array."
					);
			}
			string refstring = ToAwkString(@ref);
			return Isinref(refstring, key);
		}

		internal static int Isinref(string refstring, object key)
		{
			object o = null;
            if(instance.reference_map.ContainsKey(refstring))
            {
                o = instance.reference_map[refstring];
            }
			if (o == null)
			{
				throw new org.jawk.jrt.IllegalAwkArgumentException("Invalid refstring : " + refstring
					);
			}
			org.jawk.jrt.AssocArray aa = (org.jawk.jrt.AssocArray)o;
			return aa.IsIn(key) ? ONE : ZERO;
		}

		private void Dumprefs()
		{
			foreach (string o1 in reference_map.Keys)
			{
				object o2 = reference_map[o1];
				
				if (o2 is org.jawk.jrt.AssocArray)
				{
					o2 = ((org.jawk.jrt.AssocArray)o2).MapString();
				}
				System.Console.Out.WriteLine("REF : " + o1 + " = " + o2);
			}
		}

		internal static string Typeof(object arg, org.jawk.jrt.IVariableManager vm)
		{
			{
				if (arg is org.jawk.jrt.AssocArray)
				{
					return "AssocArray";
				}
				else
				{
					if (arg is int)
					{
						return "Integer";
					}
					else
					{
						if (arg is double)
						{
							return "Double";
						}
						else
						{
							string string_rep = instance.ToAwkString(arg);
							if (instance.reference_map.ContainsKey(string_rep))
							{
								return "Reference";
							}
							else
							{
								return "String";
							}
						}
					}
				}
			}
		}

		private int Get(org.jawk.jrt.AssocArray retval, org.jawk.jrt.AssocArray map, object
			 key)
		{
			retval.Clear();
			retval.Put(0, map.Get(key));
			return 1;
		}

		private object Toscalar(org.jawk.jrt.AssocArray aa)
		{
			return aa.Get(0);
		}

		private object Map(object[] args, org.jawk.jrt.IVariableManager vm, int map_type)
		{
			if (args.Length % 2 == 0)
			{
				return Submap(args, vm, map_type);
			}
			else
			{
				return Toplevelmap(args, vm, map_type, false);
			}
		}

		// false = map assignment
		private object Mapunion(object[] args, org.jawk.jrt.IVariableManager vm, int map_type
			)
		{
			return Toplevelmap(args, vm, map_type, true);
		}

		// true = map union
		private int Toplevelmap(object[] args, org.jawk.jrt.IVariableManager vm, int map_type
			, bool map_union)
		{
			org.jawk.jrt.AssocArray aa = (org.jawk.jrt.AssocArray)args[0];
			if (!map_union)
			{
				aa.Clear();
				aa.UseMapType(map_type);
			}
			int cnt = 0;
			for (int i = 1; i < args.Length; i += 2)
			{
				if (args[i] is org.jawk.jrt.AssocArray)
				{
					args[i] = Newreference(args[i]);
				}
				if (args[i + 1] is org.jawk.jrt.AssocArray)
				{
					args[i + 1] = Newreference(args[i + 1]);
				}
				aa.Put(args[i], args[i + 1]);
				++cnt;
			}
			return cnt;
		}

		private org.jawk.jrt.AssocArray Submap(object[] args, org.jawk.jrt.IVariableManager
			 vm, int map_type)
		{
			org.jawk.jrt.AssocArray aa = new org.jawk.jrt.AssocArray(false);
			aa.UseMapType(map_type);
			for (int i = 0; i < args.Length; i += 2)
			{
				if (args[i] is org.jawk.jrt.AssocArray)
				{
					args[i] = Newreference(args[i]);
				}
				if (args[i + 1] is org.jawk.jrt.AssocArray)
				{
					args[i + 1] = Newreference(args[i + 1]);
				}
				aa.Put(args[i], args[i + 1]);
			}
			return aa;
		}

		private int Array(object[] args, org.jawk.jrt.IVariableManager vm)
		{
			org.jawk.jrt.AssocArray aa = (org.jawk.jrt.AssocArray)args[0];
			aa.Clear();
			aa.UseMapType(org.jawk.jrt.AssocArray.MT_TREE);
			string subsep = ToAwkString(vm.GetSUBSEP());
			int cnt = 0;
			for (int i = 1; i < args.Length; ++i)
			{
				object o = args[i];
				if (o is org.jawk.jrt.AssocArray)
				{
					org.jawk.jrt.AssocArray arr = (org.jawk.jrt.AssocArray)o;
					foreach (object key in arr.KeySet())
					{
						aa.Put(string.Empty + i + subsep + key, arr.Get(key));
					}
				}
				else
				{
					aa.Put(string.Empty + i, o);
				}
				//aa.put(args[i], args[i+1]);
				++cnt;
			}
			return cnt;
		}

		private int Mapcopy(object[] args)
		{
			org.jawk.jrt.AssocArray aa_target = (org.jawk.jrt.AssocArray)args[0];
			org.jawk.jrt.AssocArray aa_source = (org.jawk.jrt.AssocArray)args[1];
			aa_target.Clear();
			int cnt = 0;
			foreach (object o in aa_source.KeySet())
			{
				aa_target.Put(o, aa_source.Get(o));
				++cnt;
			}
			return cnt;
		}

		private object Todouble(object arg, org.jawk.jrt.IVariableManager vm)
		{
			if (arg is org.jawk.jrt.AssocArray)
			{
				throw new System.ArgumentException("Cannot deduce double value from an associative array."
					);
			}

            double d = 0;
			if (double.TryParse(arg.ToString(),out d))
			{
				return d;
			}
			// otherwise, a string
			try
			{
				string str = ToAwkString(arg);
                d = double.Parse(str, System.Globalization.CultureInfo.InvariantCulture.NumberFormat);
				return d;
			}
			catch (FormatException)
			{
				return string.Empty;
			}
		}

		private static string Tostring(object arg, org.jawk.jrt.IVariableManager vm)
		{
			if (arg is org.jawk.jrt.AssocArray)
			{
				return ((org.jawk.jrt.AssocArray)arg).MapString();
			}
			else
			{
				return instance.ToAwkString(arg);
			}
		}

		private int wait_int = 0;

		private sealed class _BlockObject_653 : org.jawk.jrt.BlockObject
		{
			public _BlockObject_653(CoreExtension _enclosing)
			{
				this._enclosing = _enclosing;
			}

			public override string GetNotifierTag()
			{
				return "Timeout";
			}

			/// <exception cref="System.Exception"></exception>
			public sealed override void Block()
			{
				lock (this._enclosing.timeout_blocker)
				{
					Monitor.Wait(this._enclosing.timeout_blocker, this._enclosing.wait_int);
				}
			}

			private readonly CoreExtension _enclosing;
		}

		private org.jawk.jrt.BlockObject timeout_blocker;

		private object Timeout(int ms)
		{
			if (ms <= 0)
			{
				throw new org.jawk.jrt.IllegalAwkArgumentException("Timeout requires a positive # argument, not "
					 + ms + ".");
			}
			wait_int = ms;
			return timeout_blocker;
		}

		private string Version(object obj)
		{
			if (obj is org.jawk.jrt.AssocArray)
			{
				return ((org.jawk.jrt.AssocArray)obj).GetMapVersion();
			}
			else
			{
                return obj.GetType().Assembly.ImageRuntimeVersion;
			}
		}

		

		// single threaded, so one Date object (unsynchronized) will do
		private string Date()
		{
			return DateTime.Now.ToString();
		}

		private string Date(string format_string)
		{
            return DateTime.Now.ToString(format_string);
		}

		private int Fileexists(string path)
		{
            if (System.IO.File.Exists(path))
			{
				return ONE;
			}
			else
			{
				return ZERO;
			}
		}
	}
}
